﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Substitute.Base;
using Substitute.Loggers;
using Substitute.Readers;

namespace Substitute
{
    class Program
    {
        static int Main(string[] args)
        {
            Base.Logger logger = null;

            // Mode Delimiter VariableFile OutputFile Sections..
            if (args.Length < 5)
            {
                ShowUsage();
                return -1;
            }

            if (args.Last() == "/v")
                logger = new StdOutLogger();
            else
                logger = new DummyLogger();

            logger.WriteLog(@"Main : Starting up");

            string mode = args[0];
            string delimiter = args[1];
            string variableFile = args[2];
            string substituteFile = args[3];

            if ((!File.Exists(variableFile)) || (!File.Exists(substituteFile)))
            {
                ShowError(@"Unable to open file(s).");
                return -1;
            }

            VariableReader reader = GetReader(mode.ToLower());

            logger.WriteLog(String.Format(@"Main : Mode - {0}", mode.ToLower()));

            if (reader == null)
            {
                logger.WriteLog(@"Main : Invalid mode");
                ShowUsage();
                return -1;
            }

            if (!reader.LoadVariables(variableFile, logger))
            {
                ShowError(@"Unable to parse variable file.");
                return -1;
            }

            logger.WriteLog(String.Format(@"Main : Reading substitute file - {0}", substituteFile));
            string substituteContents = File.ReadAllText(substituteFile);
            logger.WriteLog(@"Main : Read complete");

            var sectionEnumerator = args.Skip(4).GetEnumerator();
            while (sectionEnumerator.MoveNext())
            {
                string currentKey = sectionEnumerator.Current.ToUpper();
                logger.WriteLog(String.Format(@"Main : Substituting section - {0}", currentKey));
                if ((currentKey != "/V") && (reader.Sections.ContainsKey(currentKey)))
                {
                    SubstituteSection(delimiter, ref substituteContents, reader.Sections[currentKey], logger);
                }
            }

            logger.WriteLog(String.Format(@"Main : Writing file - {0}", substituteFile));
            try
            {
                File.WriteAllText(substituteFile, substituteContents);
            }
            catch (Exception ex)
            {
                // Swallow everything.. 
                ShowError(String.Format(@"Unable to save to substitution file : {0}", ex.Message));
            }
            logger.WriteLog(@"Main : Complete");
            return 0;
        }

        private static void ShowError(string p)
        {
            Console.WriteLine(@"Error!");
            Console.WriteLine(p);
        }

        private static void SubstituteSection(string delimiter, ref string substituteContents, Dictionary<string, string> variables, Base.Logger logger)
        {
            var variableEnumerator = variables.GetEnumerator();
            while (variableEnumerator.MoveNext())
            {
                string variableName = String.Format("{0}{1}{0}", delimiter, variableEnumerator.Current.Key);
                string variableValue = variableEnumerator.Current.Value;

                logger.WriteLog(String.Format(@"Substitution : Substituting {0} with {1}", variableName, variableValue));
                substituteContents = Regex.Replace(substituteContents, variableName, variableValue, RegexOptions.IgnoreCase);
            }
        }

        private static void ShowUsage()
        {
            string appName = Environment.GetCommandLineArgs()[0];

            string modes = "\t[mode] - Mode to use (";
            var typeEnumerator = GetReaderTypes().GetEnumerator();
            while (typeEnumerator.MoveNext())
            {
                modes += typeEnumerator.Current + ',';
            }
            modes = modes.Trim(',') + ")";

            Console.WriteLine("");
            Console.WriteLine("Substitute - Variable Substitution Utility");
            Console.WriteLine("(http://substitute.codeplex.com)");
            Console.WriteLine("");
            Console.WriteLine("Usage:");
            Console.WriteLine(String.Format("\t{0} [mode] [delimiter] [variablefile.txt] [substitutionfile.txt] [sections..] (/v)", appName));
            Console.WriteLine();
            Console.WriteLine(modes);
            Console.WriteLine("\t[delimiter] - Variable delimiter (e.g. % would replace %VARIABLE%)");
            Console.WriteLine("\t[variablefile.txt] - File containing variables for substituting");
            Console.WriteLine("\t[substitutionfile.txt] - File to have it's contents substituted (%VARIABLENAME% syntax)");
            Console.WriteLine("\t[sections..] - List of sections in the variable file to use");
            Console.WriteLine("\t(/v) - (optional) Verbose output");
        }

        private static IEnumerable<String> GetReaderTypes()
        {
            Type classType = typeof(Base.VariableReader);
            Type attributeType = typeof(Base.ReaderHandles);

            var types = AppDomain.CurrentDomain.GetAssemblies()
                        .SelectMany(s => s.GetTypes())
                        .Where((p => (classType.IsAssignableFrom(p) && (p.GetCustomAttributes(attributeType, false).Count() > 0) && (p.IsClass == true))))
                        .Select(t => ((Base.ReaderHandles)t.GetCustomAttributes(attributeType, false)[0]).Handles);

            return types;
        }

        private static VariableReader GetReader(string handles)
        {
            Type classType = typeof(Base.VariableReader);
            Type attributeType = typeof(Base.ReaderHandles);

            var types = AppDomain.CurrentDomain.GetAssemblies()
                        .SelectMany(s => s.GetTypes())
                        .Where((p => (classType.IsAssignableFrom(p) && (p.GetCustomAttributes(attributeType, false).Count() > 0) && (p.IsClass == true) && (((Base.ReaderHandles)p.GetCustomAttributes(attributeType, false)[0]).Handles == handles))));

            var readerType = types.FirstOrDefault();
            if (readerType == null)
                return null;
            else
                return (VariableReader)Activator.CreateInstance(readerType);
        }

    }
}
